﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections;

namespace MassIntellect
{
    public class Condition
    {
        protected Func<string> GetName;
        public virtual bool GetResult()
        {
            return _Is;
        }
        protected bool _Is;
        public virtual bool Is
        {
            get
            {
                return _Is;
            }
        }
        public override string ToString()
        {
            return GetName != null ? GetName() : base.ToString();
        }
        /// <summary>
        /// Фабрика шаблонов
        /// </summary>
        public struct Templates
        {
            public class AlwaysOneResult: Condition
            {
                private bool result;
                public AlwaysOneResult(bool res)
                {
                    this.result = res;
                }
                public override bool GetResult()
                {
                    return result;
                }
                public override bool Is
                {
                    get
                    {
                        return result;
                    }
                }
            }
            public static readonly Condition TRUE = new AlwaysOneResult(true);
            public static readonly Condition FALSE = new AlwaysOneResult(false);

            public static Condition Group(op Op, bool reducedCalc, params object[] conds)
            {
                return new Group(Op, reducedCalc, conds);
            }

            /// <summary>
            /// Шаблон условия, останавливающего поток на заданное кол-во миллисекунд и возращающее false
            /// </summary>
            /// <param name="Interval">Интервал в секундах</param>
            /// <returns>Созданный шаблон</returns>
            public static Condition Pause(int Interval)
            {
                return Condition.Factory.Create(
                        () =>
                        {
                            Thread.Sleep(Interval);
                            return false;
                        },
                        () => "Пауза миллисекунд: {0}".goFormat(Interval)
                        );
            }
            public static Condition ForEachSeconds(int Interval)
            {
                return Condition.Factory.Create<DateTime>(DateTime.Now,
                        () => DateTime.Now,
                        (e, i) => Math.Abs((e - i).TotalSeconds) >= Interval);
            }
            public static Condition ForEachMilliSeconds(int Interval)
            {
                return Condition.Factory.Create<DateTime>(DateTime.Now,
                        () => DateTime.Now,
                        (e, i) => Math.Abs((e - i).TotalMilliseconds) >= Interval);
            }
        }

        public enum op
        {
            AND,
            OR,
            XOR
        }

        internal class Group : Condition
        {
            public op Op;
            public bool ReducedCalc;
            public override bool GetResult()
            {
                _Is = List.Count == 0 || List[0].GetResult();
                if (List.Count > 1)
                    if (Op == op.XOR)
                        for (int i = 1; i < List.Count; i++)
                            _Is ^= List[i].GetResult();
                    else
                        if (ReducedCalc)
                            if (Op == op.AND)
                            {
                                for (int i = 1; i < List.Count; i++)
                                    if (!(_Is = _Is && List[i].GetResult()))
                                        break;
                            }
                            else
                            {
                                for (int i = 1; i < List.Count; i++)
                                    if (_Is = _Is || List[i].GetResult())
                                        break;
                            }
                        else
                            if (Op == op.AND)
                                for (int i = 1; i < List.Count; i++)
                                    _Is = _Is & List[i].GetResult();
                            else
                                for (int i = 1; i < List.Count; i++)
                                    _Is = _Is | List[i].GetResult();
                return _Is;
            }

            public override bool Is
            {
                get
                {
                    _Is = List.Count == 0 || List[0].Is;
                    if (List.Count > 1)
                        if (Op == op.XOR)
                            for (int i = 1; i < List.Count; i++)
                                _Is ^= List[i].Is;
                        else
                            if (Op == op.AND)
                            {
                                for (int i = 1; i < List.Count; i++)
                                    if (!(_Is = _Is && List[i].Is))
                                        break;
                            }
                            else
                            {
                                for (int i = 1; i < List.Count; i++)
                                    if (_Is = _Is || List[i].Is)
                                        break;
                            }
                    return _Is;
                }
            }

            public readonly List<Condition> List = new List<Condition>();

            private void AddRange(IEnumerable conds)
            {
                Condition c;
                IEnumerable e;
                if (conds != null)
                    foreach (var item in conds)
                        if ((c = item as Condition) != null)
                            List.Add(c);
                        else if ((e = item as IEnumerable) != null)
                            AddRange(e);
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="op">Оператор</param>
            /// <param name="ReducedCalc">Сокращенные вычисления</param>
            /// <param name="conds"></param>
            public Group(op op, bool ReducedCalc, params object[] conds)
            {
                this.Op = op;
                this.ReducedCalc = ReducedCalc;
                AddRange(conds);
            }
        }

        public struct Factory
        {
            /// <summary>
            /// Условие на основе булевой функции
            /// </summary>
            /// <param name="Func">Функция условия</param>
            /// <returns></returns>
            public static Condition Create(Func<bool> Func)
            {
                if (Func != null)
                    return new FuncCondition() { Func = Func };
                else
                    throw new ArgumentNullException();
            }
            /// <summary>
            /// Условие на основе булевой функции
            /// </summary>
            /// <param name="Func">Функция условия</param>
            /// <param name="GetName">Функция отображаемого имени</param>
            /// <returns></returns>
            public static Condition Create(Func<bool> Func, Func<string> GetName)
            {
                if (Func != null)
                    return new FuncCondition() { Func = Func, GetName = GetName };
                else
                    throw new ArgumentNullException();
            }
            /// <summary>
            /// Создание условия с состоянием. Если при проверке условия по состоянию оно выполняется, то условие "переходит" в проверенное состояние.
            /// </summary>
            /// <param name="StartState">Начальное состояние.</param>
            /// <param name="GetStateFunc">Функция получения текущего независимого (без параметров) состояния.</param>
            /// <param name="CompareFunc">Функция сравнения состояний.</param>
            /// <returns></returns>                
            public static Condition Create<T>(T StartState, Func<T> GetStateFunc, Func<T, T, bool> CompareFunc)
            {
                if (GetStateFunc != null && CompareFunc != null)
                    return new Condition<T>() { State = StartState, GetStateFunc = GetStateFunc, CompareFunc = CompareFunc };
                else
                    throw new ArgumentNullException();
            }
        }

        public static explicit operator bool(Condition cond)
        {
            return cond.Is;
        }
    }


    public class FuncCondition : Condition
    {
        internal Func<bool> Func;
        public override bool GetResult()
        {
            return _Is = Func();
        }
        internal FuncCondition() { }

    }
    public class Condition<T> : Condition
    {
        public T State;
        public Func<T> GetStateFunc;
        public Func<T, T, bool> CompareFunc;
        public override bool GetResult()
        {
            T s = GetStateFunc();
            if (CompareFunc(s, State))
            {
                this.State = s;
                return _Is = true;
            }
            else
                return _Is = false;
        }
        public Condition() { }
    }
}
